package com.jl.interceptor;

import com.jl.ReflectUtils;
import com.jl.Tuple;
import com.jl.interceptor.annotate.Interceptor;
import com.jl.set.list.JArrayList;
import com.jl.set.list.JList;
import lombok.Builder;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.context.ApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * 拦截器
 */
@Slf4j
@RequiredArgsConstructor
public class InterceptorImpl extends HandlerInterceptorAdapter implements WebMvcConfigurer {

    private final ApplicationContext applicationContext;

    private JList<InterceptorParam> list;

    /**
     * 方法执行前
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
            HandlerMethod handler2 = (HandlerMethod) handler;
            //这里判断异常的不重复执行拦截器
            if (BasicErrorController.class.equals(handler2.getBeanType())) {
                return true;
            }
        }
        //这里判断静态资源的不重复执行拦截器
        if (handler.getClass().isAssignableFrom(ResourceHttpRequestHandler.class)) {
            return true;
        }
        return exec(request.getRequestURI(), 0);
    }

    /**
     * 方法执行后
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        exec(request.getRequestURI(), 1);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this)
                .addPathPatterns("/**");
        //缓存
        JList<InterceptorParam> jList = new JArrayList<>();
        String[] beans = applicationContext.getBeanDefinitionNames();
        for (String bean : beans) {
            Object beanObj;
            try {
                beanObj = applicationContext.getBean(bean);
            } catch (Exception e) {
                continue;
            }
            List<Tuple.Tuple2<Method, Interceptor>> methods = ReflectUtils.MethodReflect.getMethod(beanObj.getClass(), Interceptor.class);
            for (Tuple.Tuple2<Method, Interceptor> methodObj : methods) {
                Method method = methodObj.getV1();
                Interceptor interceptor = methodObj.getV2();
                InterceptorParam interceptorParam = InterceptorParam.builder()
                        .bean(beanObj)
                        .method(method)
                        .methodParams(ReflectUtils.MethodReflect.checkParamType(method.getParameters(), interceptor.values()))
                        .order(interceptor.order())
                        .excludesList(Arrays.asList(interceptor.excludes()))
                        .error(interceptor.error())
                        .before(interceptor.before())
                        .after(interceptor.after())
                        .build();
                jList.add(interceptorParam);
            }
        }
        list = jList.asc(InterceptorParam::getOrder);
    }

    /**
     * 执行拦截器逻辑
     */
    private boolean exec(String requestURI, int type) {
        for (InterceptorParam interceptorParam : list) {
            boolean fal = type == 0 && interceptorParam.getBefore() ? true
                    : type == 1 && interceptorParam.getAfter() ? true
                    : false;
            if (fal) {
                List<String> excludesList = interceptorParam.getExcludesList();
                if (excludesList.contains(requestURI)) {
                    continue;
                }
                Method method = interceptorParam.getMethod();
                Object invoke;
                try {
                    invoke = method.invoke(interceptorParam.getBean(), interceptorParam.getMethodParams());
                } catch (Exception e) {
                    log.error("拦截器执行异常：{}", e);
                    return false;
                }
                if (invoke != null && invoke instanceof Boolean) {
                    if (!Boolean.parseBoolean(invoke.toString())) {
                        throw new ExceptionInfo(interceptorParam.getError());
                    }
                }
            }
        }
        return true;
    }

    @Data
    @Builder
    public static class InterceptorParam {

        private Object bean;

        private Method method;

        private Object[] methodParams;

        private List<String> excludesList;

        private String error;

        private Boolean before;

        private Boolean after;

        private int order;
    }

    public static class ExceptionInfo extends RuntimeException {

        public ExceptionInfo(String message) {
            super(message);
        }

    }
}
